package com.thwj.project.app.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.thwj.common.exception.ServiceException;
import com.thwj.common.utils.WzDateUtil;
import com.thwj.framework.security.LoginUser;
import com.thwj.project.app.api.param.UserAccountWithdrawParam;
import com.thwj.project.app.api.vo.UserAccountInOutVo;
import com.thwj.project.app.api.vo.UserAccountVo;
import com.thwj.project.biz.domain.User;
import com.thwj.project.biz.domain.UserAccount;
import com.thwj.project.biz.domain.UserAccountInout;
import com.thwj.project.biz.domain.UserAccountWithdraw;
import com.thwj.project.biz.enums.*;
import com.thwj.project.biz.service.IUserAccountInoutService;
import com.thwj.project.biz.service.IUserAccountService;
import com.thwj.project.biz.service.IUserAccountWithdrawService;
import com.thwj.project.biz.service.IUserService;
import com.thwj.project.push.enums.PushTemplateEnumNew;
import com.thwj.project.push.event.WxMpAdminMessageEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
@Transactional
@Service
public class AppAccountService {

    @Autowired
    IUserService userService;
    @Autowired
    IUserAccountService userAccountService;
    @Autowired
    ApplicationEventPublisher applicationEventPublisher;
    @Autowired
    IUserAccountWithdrawService userAccountWithdrawService;
    @Autowired
    IUserAccountInoutService userAccountInoutService;

    public UserAccountVo getUserAccount(LoginUser loginUser) {
        UserAccount userAccount = userAccountService.getOne(
                new QueryWrapper<UserAccount>()
                        .eq("user_id", loginUser.getUserId()),false);
        UserAccountVo userAccountVo = new UserAccountVo();
        if(userAccount == null) {
            return userAccountVo;
        }
        userAccountVo.setAmount(userAccount.getAmount());
        userAccountVo.setFreezeAmount(userAccount.getFreezeAmount());
        userAccountVo.setWithdrawAmount(userAccount.getAmount());
        return userAccountVo;
    }

    public Page<UserAccountInOutVo> getUserAccountInOutPage(Page page, LoginUser loginUser) {
        //  读取用户的账单明细数据
        Page<UserAccountInout> inoutPage =  userAccountInoutService.page(page,new QueryWrapper<UserAccountInout>()
                .eq("user_id",loginUser.getUserId()).orderByDesc("create_date")
        );
        List<UserAccountInOutVo> inOutVoList = new ArrayList<>();
        for (UserAccountInout record : inoutPage.getRecords()) {
            UserAccountInOutVo inOutVo = new UserAccountInOutVo();
            inOutVo.setInOutType(record.getInOutType());
            inOutVo.setAmount(record.getAmount());
            inOutVo.setSourceName(record.getSourceName());
            inOutVo.setDate(WzDateUtil.date2String(record.getCreateDate(),WzDateUtil.TIME_FORMAT_H));
            AccountInoutStatusEnum statusEnum = AccountInoutStatusEnum.getEnumByValue(record.getStatus());
            inOutVo.setStatusName(statusEnum.getName());
            inOutVoList.add(inOutVo);
        }
        page.setRecords(inOutVoList);
        return page;
    }

    public void commitWithdrawAudit(UserAccountWithdrawParam withdrawParam, LoginUser loginUser) {

        User user = userService.getById(loginUser.getUserId());
        if( user == null || UserStatusEnum.forbidden.getValue() == user.getStatus()) {
            throw new ServiceException("账户异常，联系客服处理");
        }
        // 读取当前账户的可用余额
        UserAccount userAccount = userAccountService.getOne(
                new QueryWrapper<UserAccount>()
                        .eq("user_id", loginUser.getUserId()),false);

       if(userAccount == null || userAccount.getAmount() == null || userAccount.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("可提现金额必须大于0");
        }
        if (userAccount.getAmount().doubleValue() < withdrawParam.getWithdrawAmount().doubleValue()) {
            throw new ServiceException("提现金额超过可用金额");
        }
        if (withdrawParam.getWithdrawAmount().doubleValue() > 5000) {
            throw new ServiceException("微信限制零钱提现必须小于5000元");
        }
        // 写入审核记录表
        UserAccountWithdraw withdraw = new UserAccountWithdraw();
        withdraw.setAmount(withdrawParam.getWithdrawAmount());
        withdraw.setCommission(withdrawParam.getWithdrawAmount().multiply(new BigDecimal(0.006)).setScale(2, RoundingMode.HALF_UP));
        withdraw.setActualAmount(withdraw.getAmount().subtract(withdraw.getCommission()));
        withdraw.setCreateDate(new Date());
        withdraw.setStatus(AccountWithdrawStatusEnum.auditing.getValue());
        withdraw.setUpdateDate(new Date());
        withdraw.setUserId(loginUser.getUserId());
        userAccountWithdrawService.save(withdraw);
        // 写入 账户表
        UserAccountInout inout = this.saveUpdateAccountInout(loginUser.getUserId(),AccountSourceTypeEnum.withdraw,withdraw.getId(),"提现到零钱",withdrawParam.getWithdrawAmount(),AccountInoutStatusEnum.auditing);

        // 发送微信通知给审核人员
        applicationEventPublisher.publishEvent(WxMpAdminMessageEvent.builder()
                .pushTemplateEnum(PushTemplateEnumNew.WITHDRAW)
                .templateDataList(PushTemplateEnumNew.WITHDRAW(
                        user.getUserNickName(),
                        withdraw.getAmount(),
                        withdraw.getActualAmount(),
                        "线下转账",
                        "处理中"
                        )
                ).build()
        );
    }


    /***
     * 新增账户明细 这里不包括 对于生成后的数据状态进行调整
     * @param userId
     * @param sourceTypeEnum
     * @param amount
     * @param statusEnum
     */
    public UserAccountInout saveUpdateAccountInout(Long userId, AccountSourceTypeEnum sourceTypeEnum,Long sourceId,String sourceName, BigDecimal amount, AccountInoutStatusEnum statusEnum) {
        // 读取当前账户的可用余额
        UserAccount userAccount = userAccountService.getOne(
                new QueryWrapper<UserAccount>()
                        .eq("user_id", userId),false);
        if( userAccount == null) {
            userAccount = new UserAccount();
            userAccount.setUserId(userId);
            userAccount.setAmount(BigDecimal.ZERO);
            userAccount.setFreezeAmount(BigDecimal.ZERO);
            userAccount.setCreateDate(new Date());
        }
        // 出账 不管什么状态 先把可用金额扣减到 入账中的也是先扣减  审核通过就不在更新可用金额字段  审核失败 退回可用金额
        if (AccountInoutTypeEnum.out.getValue() == sourceTypeEnum.getInoutTypeEnum().getValue()) {
            if(userAccount.getAmount().subtract(amount).compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("账户余额不足");
            }
            userAccount.setAmount(userAccount.getAmount().subtract(amount));
        }
        // 紧张除非明确已到账 入账中的都是先放冻结账户不可用 等时效到了或者审核过了才可以
        else {
            // 入账中 都是先到冻结账户然后才会到可用账户
            if (AccountInoutStatusEnum.auditing.getValue() == statusEnum.getValue()) {
                userAccount.setFreezeAmount(userAccount.getFreezeAmount().add(amount));
            }
            if (AccountInoutStatusEnum.Received.getValue() == statusEnum.getValue()) {
                userAccount.setAmount(userAccount.getAmount().add(amount));
            }
        }
        // 乐观锁 问题
        if (!userAccountService.saveOrUpdate(userAccount)) {
            throw new ServiceException("数据已过期，请重新操作");
        }
        UserAccountInout accountInout = new UserAccountInout();
        accountInout.setAmount(amount);
        accountInout.setCreateDate(new Date());
        accountInout.setUserId(userId);
        accountInout.setInOutType(sourceTypeEnum.getInoutTypeEnum().getValue());
        accountInout.setSourceType(sourceTypeEnum.getValue());
        accountInout.setSourceId(sourceId);
        accountInout.setSourceName(sourceName);
        accountInout.setStatus(statusEnum.getValue());
        try {
            userAccountInoutService.save(accountInout);
        }catch (DuplicateKeyException e){
            throw new ServiceException("请重新操作");
        }
        return accountInout;
    }

}
